<template>
  <div id="renderer" ref="renderer" @click.stop.prevent="onClick" style="height:100%;width:100%">
    <slot></slot>
  </div>
</template>

<script>
import * as Three from "three";
import TWEEN from "@tweenjs/tween.js";
let OrbitControls = require("three-orbit-controls")(Three);

export default {
  name: "renderer",
  props: {
    models: { type: Object },
    location: { type: Array, default: () => [1, 1, 1] },
    scale: { type: Array, default: () => [1, 1, 1] },
    cameraState: { type: Object }
  },
  data() {
    return {
      renderer: null,
      scene: null,
      camera: null,
      controls: null,
      container: null,
      clock: null,
      width: 0,
      height: 0,
      persons: [],
      object3d: null,
      raycaster: null,
      mouse: new Three.Vector2()
    };
  },
  watch: {
    cameraState() {
      if (this.cameraState) {
        this.camera.position.copy(this.cameraState.cameraPosition);
        this.controls.target.copy(this.cameraState.targetPosition);
      }
    },
    building() {
      if (this.models && this.models.scene) {
        this.object3d = this.models.scene;
        this.object3d.position.set(
          this.location[0],
          this.location[1],
          this.location[2]
        );
        this.object3d.scale.set(this.scale[0], this.scale[1], this.scale[2]);
        this.scene.add(this.object3d);
      }
    }
  },
  computed: {
    building() {
      return this.models.scene;
    }
  },
  mounted() {
    this.initScene();
    this.initRenderer();
    this.initCamera();
    this.initLight();
    this.initControls();
    this.animate();
  },
  methods: {
    initScene() {
      this.scene = new Three.Scene();
    },
    initCamera() {
      this.camera = new Three.PerspectiveCamera(
        45,
        this.width / this.height,
        1,
        1000
      );
      this.camera.position.set(0, 40, 0);
      this.camera.lookAt(new Three.Vector3(0, 0, 0));
    },
    initRenderer() {
      this.clock = new Three.Clock();
      this.container = document.getElementById("renderer");
      this.width = this.container.clientWidth;
      this.height = this.container.clientHeight;
      this.renderer = new Three.WebGLRenderer({
        antialias: true,
        alpha: this.alpha
      });

      this.renderer.setClearColor(0x000927, 0);
      this.renderer.setPixelRatio(window.devicePixelRatio);
      this.renderer.setSize(this.width, this.height);
      // 启用光照阴影
      this.renderer.shadowMap.enabled = true;
      this.renderer.gammaInput = true;
      this.renderer.gammaOutput = true;

      this.raycaster = new Three.Raycaster();

      window.addEventListener("resize", this.resize);

      this.container.appendChild(this.renderer.domElement);
    },
    initLight() {
      let hemiLight = new Three.HemisphereLight(0xffffff, 0x444444, 0.2);
      hemiLight.position.set(0, 20, 0);
      this.scene.add(hemiLight);
      let dirLight = new Three.DirectionalLight(0xffffff);
      dirLight.position.set(-3, 10, -10);
      dirLight.castShadow = true;
      dirLight.shadow.camera.top = 2;
      dirLight.shadow.camera.bottom = -2;
      dirLight.shadow.camera.left = -2;
      dirLight.shadow.camera.right = 2;
      dirLight.shadow.camera.near = 0.1;
      dirLight.shadow.camera.far = 40;
      this.scene.add(dirLight);
    },
    initControls() {
      this.controls = new OrbitControls(this.camera, this.container);
      this.controls.minDistance = 5;
      this.controls.maxDistance = 500;
      this.controls.mouseButtons = {
        ORBIT: Three.MOUSE.RIGHT,
        ZOOM: Three.MOUSE.MIDDLE,
        PAN: Three.MOUSE.LEFT
      };
      this.controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled
      this.controls.dampingFactor = 0.5;
      this.controls.maxPolarAngle = Math.PI / 2.5;

      this.controls.update();
    },
    animate() {
      requestAnimationFrame(this.animate);
      if (this.renderer) {
        this.controls.update();
        let mixerUpdateDelta = this.clock.getDelta();
        this.persons.forEach(person => {
          if (person.mixer) {
            person.mixer.update(mixerUpdateDelta);
          }
        });
        this.renderer.render(this.scene, this.camera);
        // this.composer.render();
        TWEEN.update();
      }
    },
    pick(event) {
      let { scaleWid, scaleHei } = this.computedScale();
      let left = this.renderer.domElement.offsetLeft * scaleWid,
        top = this.renderer.domElement.offsetTop * scaleHei,
        width = this.renderer.domElement.clientWidth * scaleWid,
        height = this.renderer.domElement.clientHeight * scaleHei;
      this.mouse.x = ((event.clientX - left) / width) * 2 - 1;
      this.mouse.y = -((event.clientY - top) / height) * 2 + 1;
      this.raycaster.setFromCamera(this.mouse, this.camera);
      let intersects = this.raycaster.intersectObjects(
        this.scene.children,
        true
      );
      return intersects;
    },
    onClick(event) {
      this.clickTimer = setTimeout(_ => {
        let intersected = this.pick(event);
        this.$emit("on-click", event, intersected);
      }, 300);
    },
    resize() {
      let re = document.getElementById("renderer");
      let width = re.clientWidth;
      let height = re.clientHeight;
      this.renderer.setSize(width, height, true);
    },
    computedScale() {
      let wid = window.innerWidth;
      let hei = window.innerHeight;
      let scaleWid = wid / 1920;
      let scaleHei = hei / 1080;
      return { scaleWid, scaleHei };
    },
  },
  beforeDestroy() {
    console.log("renderer destroy");
    this.controls.dispose();
    this.renderer.dispose();
    delete this.camera;
    delete this.controls;
    delete this.renderer;
    this.scene.dispose();
    delete this.scene;
  }
};
</script>

<style>
</style>